package com.mylogistics.common.security;

import com.mylogistics.common.Utils.JWTUtil;
import com.mylogistics.common.Utils.ResponseUtil;
import com.mylogistics.common.filter.MySecurityJWTFilter;
import com.mylogistics.common.vo.ResultData;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;


@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    //设置加密方式
    public DaoAuthenticationProvider authenticationProvider() {
        //创建自定义的操作dao
        DaoAuthenticationProvider authProvider
                = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);

        //设置密码编码加密
        authProvider.setPasswordEncoder(new BCryptPasswordEncoder());
        return authProvider;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //放行swagger
        //放行注册
        web.ignoring().antMatchers(HttpMethod.GET.toString(),
                "/v2/api-docs",
                "/swagger-resources",
                "/swagger-resources/**",
                "/configuration/ui",
                "/configuration/security",
                "/swagger-ui.html/**",
                "/doc.html/**",
                "/webjars/**",
                "/wx",
                "/wxchat/**",
                "/register");
    }

    @Autowired
    MySecurityJWTFilter mySecurityJWTFilter;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //将JWT过滤器加入到Security中
        http.addFilterBefore(mySecurityJWTFilter, UsernamePasswordAuthenticationFilter.class);


        http
                .authorizeRequests()//授权配置
                .anyRequest().authenticated() //需要权限验证
                //权限处理
                .and().exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
                    @Override
                    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
                        //对结果进行处理
                        ResultData resultData = ResultData.fail("操作失败，用户没有权限", null);
                        ResponseUtil.writeJSON(httpServletResponse, resultData);
                    }
                })
                .and()
                .formLogin() //登录认证配置
                .loginProcessingUrl("/login.action").permitAll()//登陆请求
                //登录失败处理
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(
                            HttpServletRequest httpServletRequest,
                            HttpServletResponse httpServletResponse,
                            AuthenticationException e) throws IOException, ServletException {
                        //对结果进行处理
                        ResultData resultData = ResultData.fail("登录失败，" + e.getMessage(), null);
                        ResponseUtil.writeJSON(httpServletResponse, resultData);
                    }
                })
                //登录成功处理
                .successHandler(new AuthenticationSuccessHandler() {
                    @SneakyThrows
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        UserDetails user = (UserDetails) authentication.getPrincipal();
                        //创建token
                        String token = JWTUtil.create(user.getUsername());
                        Map<String,Object> map = new HashMap<>();
                        //将token进行返回
                        map.put("token",token);
                        //对结果进行处理
                        ResultData resultData = ResultData.success("登录成功",map);
                        ResponseUtil.writeJSON(httpServletResponse,resultData);

                    }
                })
                .usernameParameter("username") //登录请求的参数必须是username
                .passwordParameter("password")
                //token是否有效判断
                .and().exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
                    @Override
                    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                        ResultData resultVo = new ResultData();
                        resultVo.setCode(ResultData.INVALIDATE_TOKEN);
                        resultVo.setMsg("账号未登录或token已经失效");
                        ResponseUtil.writeJSON(httpServletResponse, resultVo);
                    }
                })
                .and()
                .logout().permitAll()//退出功能
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//不使用session
                .and().csrf().disable();//禁用csrf
    }


}
